package cn.basic.social.api.qwtalk.client;

import cn.basic.social.api.exception.ApiException;
import cn.basic.social.api.exception.ApiRuleException;
import cn.basic.social.api.qwtalk.QwRequest;
import cn.basic.social.api.qwtalk.QwResponse;
import cn.basic.social.api.CommonConstants;
import cn.basic.social.api.qwtalk.dto.RequestDTO;
import cn.basic.social.api.utils.OkHttpUtil;
import cn.basic.social.api.utils.RequestParametersHolder;
import cn.basic.social.api.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;

/**
 * @ClassName DefaultQwTalkClient
 * @Description 企业微信默认的客户端请求接口
 * @Author zlf
 * @Date 2023/8/25 10:38
 */
@Slf4j
public class DefaultBaseQwTalkClient implements BaseQwTalkClient {

    protected boolean needCheckRequest = true; // 是否在客户端校验请求

    @Override
    public <T extends QwResponse> T doExecute(QwRequest<T> request) throws ApiException {
        return this._doExecute(request, null, null, null, null, null, null, null);
    }

    @Override
    public <T extends QwResponse> T doExecute(QwRequest<T> request, String corpid, String corpsecret) throws ApiException {
        return this._doExecute(request, corpid, corpsecret, null, null, null, null, null);
    }

    @Override
    public <T extends QwResponse> T doExecute(QwRequest<T> request, String accessToken) throws ApiException {
        return this._doExecute(request, null, null, accessToken, null, null, null, null);
    }

    @Override
    public <T extends QwResponse> T doProviderExecute(QwRequest<T> request, String providerAccessToken) throws ApiException {
        return this._doExecute(request, null, null, null, providerAccessToken, null, null, null);
    }

    @Override
    public <T extends QwResponse> T doFileExecute(QwRequest<T> request, String accessToken, String type) throws ApiException {
        return this._doExecute(request, null, null, accessToken, null, null, type, null);
    }

    @Override
    public <T extends QwResponse> T doAccessTokenExecute(QwRequest<T> request, String suiteAccessToken) throws ApiException {
        return this._doExecute(request, null, null, null, null, suiteAccessToken, null, null);
    }

    @Override
    public <T extends QwResponse> T doLoginExecute(QwRequest<T> request, String suiteAccessToken, String code) throws ApiException {
        return this._doExecute(request, null, null, null, null, suiteAccessToken, null, code);
    }

    /**
     * 最后走这里进行调用api
     *
     * @param
     * @return
     * @author zlf
     * @date 16:51 2023/9/4
     */
    protected <T extends QwResponse> T _doExecute(QwRequest<T> request, String corpid, String corpsecret, String accessToken, String providerAccessToken,
                                                String suiteAccessToken, String type, String code) throws ApiException {
        Instant start = Instant.now();

        // 对客户端传递的参数进行校验
        if (this.needCheckRequest) {
            try {
                request.check();
            } catch (ApiRuleException e) {
                T localResponse = null;
                try {
                    // 通过反射获取到返回对象实例
                    localResponse = request.getResponseClass().newInstance();
                } catch (Exception xe) {
                    throw new ApiException(xe);
                }
                localResponse.setErrorCode(e.getErrCode());
                localResponse.setErrmsg(e.getErrMsg());
                return localResponse;
            }
        }

        // 封装参数模型
        RequestParametersHolder holder = _dealExecuteApi(request, corpid, corpsecret, accessToken, providerAccessToken, suiteAccessToken, type, code, start);

        return StringUtils.isNotEmpty(holder.getResponseBody()) ? JSON.parseObject(holder.getResponseBody(), request.getResponseClass()) : null;
    }

    /**
     * 执行接口调用，封装一个RequestDTO去调用API
     *
     * @param
     * @return
     * @author zlf
     * @date 11:57 2023/8/25
     */
    protected <T extends QwResponse> RequestParametersHolder _dealExecuteApi(QwRequest<T> request, String corpid, String corpsecret, String accessToken,
                                                                           String providerAccessToken, String suiteAccessToken, String type, String code, Instant start) throws ApiException {
        RequestDTO requestDTO = new RequestDTO();
        requestDTO.setRequestUrl(request.getRequestUrl());
        requestDTO.setRequestBase(request);
        requestDTO.setHeaderMap(request.getHeaderMap());
        requestDTO.setTextParams(request.getTextParams());
        requestDTO.setContentType(request.getTopContentType());
        requestDTO.setTopHttpMethod(request.getTopHttpMethod());
        requestDTO.setFile(request.getFile());

        return _dealExecuteApi(requestDTO, corpid, corpsecret, accessToken, providerAccessToken, suiteAccessToken, type, code, start);
    }

    /**
     * 真正的调用API
     *
     * @param
     * @return
     * @author zlf
     * @date 14:24 2023/8/25
     */
    protected RequestParametersHolder _dealExecuteApi(RequestDTO requestDTO, String corpid, String corpsecret, String accessToken,
                                                    String providerAccessToken, String suiteAccessToken, String type, String code, Instant start) throws ApiException {
        RequestParametersHolder holder = new RequestParametersHolder();
        if (requestDTO.getTextParams() == null) {
            requestDTO.setTextParams(new HashMap<String, String>());
        }

        // 给参数容器添加入参
        HashMap<String, String> params = new HashMap<>(requestDTO.getTextParams());
        holder.setApplicationParams(params);

        // 给参数容器添加token级参数
        HashMap<String, String> protocalMustParams = new HashMap<>();
        protocalMustParams.put(CommonConstants.ACCESS_TOKEN, accessToken);
        holder.setProtocalMustParams(protocalMustParams);

        // 请求地址，拼接了如accesstoken的等参数
        String fullUrl = buildQueryUrl(requestDTO.getRequestUrl(),
                StringUtils.isNotEmpty(corpid) ? "corpid=" + corpid : null,
                StringUtils.isNotEmpty(corpsecret) ? "corpsecret=" + corpsecret : null,
                StringUtils.isNotEmpty(accessToken) ? "access_token=" + accessToken : null,
                StringUtils.isNotEmpty(providerAccessToken) ? "provider_access_token=" + providerAccessToken : null,
                StringUtils.isNotEmpty(suiteAccessToken) ? "suite_access_token=" + suiteAccessToken : null,
                StringUtils.isNotEmpty(type) ? "type=" + type : null,
                StringUtils.isNotEmpty(code) ? "code=" + code : null);
        holder.setRequestUrl(fullUrl);

        try {
            Response response = null;
            if (requestDTO.getFile() != null && requestDTO.getFile().length() > 0) {
                response = OkHttpUtil.getInstance().uploadFile(fullUrl, requestDTO.getFile(), requestDTO.getContentType());
            } else {
                if (requestDTO.getTopHttpMethod().equals("GET")) {
                    response = OkHttpUtil.getInstance().getData(fullUrl);
                } else if ("POST".equals(requestDTO.getTopHttpMethod())) {
                    response = OkHttpUtil.getInstance().postJson(fullUrl, JSON.toJSONString(params));
                }
            }

            if (response != null) {
                String result = response.body().string();
                holder.setResponseBody(result);
                holder.setResponseBytes(result.getBytes(StandardCharsets.UTF_8));
            }
        } catch (Exception e) {
            log.error("请求失败，请求地址：" + fullUrl + "，参数：" + params + "，耗时{" + Duration.between(start, Instant.now()).toMillis() + "}毫秒，case:" + e, e);
            throw new ApiException(e);
        }

        return holder;
    }

    /**
     * 从QwRequest中获取到参数组装成json格式，供发送API时使用
     *
     * @param qwRequest QwRequest的子类，每个API请求对应了一个qwRequest，每个qwRequest里面有当前接口的参数，用户通过set方法设置进来
     * @return
     * @author zlf
     * @date 14:13 2023/8/25
     */
    protected String getRequestBody(Object qwRequest, String type) {

        if (null == qwRequest) {
            return null;
        }

        // 获取到所有的属性
        Field[] declaredFields = qwRequest.getClass().getDeclaredFields();

        return null;
    }

    /**
     * 拼接请求地址
     *
     * @param requestUrl 原始地址，不带参数
     * @return
     * @author zlf
     * @date 11:44 2023/8/25
     */
    protected String buildQueryUrl(String requestUrl, String... params) {
        if (params == null || params.length == 0) {
            return requestUrl;
        }

        // 不适用+拼接，节省内存
        StringBuilder newUrl = new StringBuilder(requestUrl);

        boolean hasQuery = requestUrl.contains("?");
        boolean hasPrepend = requestUrl.endsWith("?") || requestUrl.endsWith("&");

        for (String query : params) {
            if (StringUtils.isNotEmpty(query)) {
                if (!hasPrepend) {
                    if (hasQuery) {
                        newUrl.append("&");
                    } else {
                        newUrl.append("?");
                        hasQuery = true;
                    }
                }
                newUrl.append(query);
                hasPrepend = false;
            }
        }

        return newUrl.toString();

    }

}
